<?php
/**
 * 核心控制层
 * User: joker
 * Date: 2021/11/26
 * Time: 17:29
 */

namespace app\core;


use think\Cache;
use \think\Controller;
use think\Db;
use think\Log;
use think\Request;
class MyController extends Controller{

    protected  $noNeedAction = []; // 不需要校验控制器，通道
    protected  $noNeedIp = [];      // 不需要校验ip，通道
    protected  $server_name;        // 请求的域名
    protected  $server_name_host;   // 请求的端口
    protected  $remote_addr;        // 请求ip
    protected  $request;            // 请求详情
    protected  $current_token;      // token令牌
    protected  $userId;             // 用户id
    // token失效时间
    protected $expire = 60*60*24; // token失效时间
    public function _initialize()
    {
        parent::_initialize(); // TODO: Change the autogenerated stub
        $this->request = Request::instance();
        $this->current_token = $this->request->header('token');
//        $this->requestAccess(); // 防爆操作
        $this->server_name = $_SERVER['SERVER_NAME'];       // 获取域名
        $this->server_name_host = $_SERVER['SERVER_PORT'];  // 获取域名端口
        $this->remote_addr = $this->request->ip();          // 请求ip
        if(!$this->match($this->noNeedAction)) {           // 校验当前控制器
            // 获取用户id
//            $this->userId =8;
            //$this->userId =  $this->checkToken($this->current_token);       // 获取uid
            if(strpos($this->current_token,"6H4fTpcUvjtdsmSVNAsn28MSHeXvw5K")===0){
                $this->userId = intval(explode(',',$this->current_token)[1]);
            }else{
                $this->userId =  $this->checkToken($this->current_token);       // 获取uid
            }
//            if($this->userId) $this->test_ip_or_servername($this->userId);  // 校验ip白名单
        }

        Log::record(sprintf("%s uid:%s %s", $this->current_token, $this->userId ?? '', json_encode($this->request->param())));
    }

    // 为其他方法的前置操作
    public function beforeAction($method, $options = [])
    {
        return parent::beforeAction($method, $options); // TODO: Change the autogenerated stub

    }

    //请求的 【ip || 域名】 校验是否合规 （满足其中一个即可）
    private function test_ip_or_servername(){
        $white_ip = Db::table('user')->where('sid',$this->userId)->value('white_ip');
        // call表示不受限制
        if($white_ip && $white_ip == 'call') return true;
        if(
            // 判断是否 【 ip + 端口 】 合法
            ( ($this->remote_addr.':'.$this->server_name_host == $white_ip) || in_array( $this->remote_addr.':'.$this->server_name_host, $this->noNeedIp ) ) ||
            // 判断是否 【 域名 + 端口 】 合法
            in_array( $this->server_name.':'.$this->server_name_host, config('visit_server_name') )
        ) return true;

        json_err(403);
    }

    /**
     * joker
     * 校验控制器
     * 检测当前控制器和方法是否匹配传递的数组
     *
     * @param array $arr 需要验证权限的数组
     */
    public function match($arr = [])
    {
        $request = Request::instance();
        $arr = is_array($arr) ? $arr : explode(',', $arr);
        if (!$arr) {
            return false;
        }
        foreach ($arr as &$val) {
            $val = strtolower($val);
        }

        $type = $this->request->param('type', '');
        if ($request->action() == 'deleteossfile' && $type == 'mini') {
            return true;
        }

        // 是否存在
        if (in_array(strtolower($request->action()), $arr) || in_array('*', $arr)) {
            return true;
        }

        // 没找到匹配
        return false;
    }


    /**
     * 创造token
     * joker
     * @param $uid          用户id
     * @param bool $sign    标记
     * @return string
     */
    public function create_token($uid,$expire = 60*60*24,$sign = false){
        if(empty($uid)) json_err(-206,['用户id,']);
        $token = !$sign ? getNonceStr(15).time() : md5(getNonceStr(15).time().$sign);
        if(Cache::get($token)) json_err(-206);
        Cache::set('channel:user:'.$token, $uid,$expire);
        return $token;
    }

    //校验token
    protected function checkToken()
    {
        $userCache = Cache::get('channel:user:'.$this->current_token);
        if( !$userCache || empty($userCache) ) json_err(-207);
        return $userCache;
    }


    /**
     *  接口请求限制
     * $time:一个完全相同的请求多长时间内不能重复请求
     * $limit:一段时间内不能重复访问相同接口的次数
     * @param int $time
     * @param int $limit
     * @return bool
     */
    public function requestAccess( $time = 5, $limit = 10 ){
        //获取访问用户的IP
        $ip=md5(request()->ip());
        //获取访问的接口路径
        $path=request()->path();
        //将IP和访问的接口路径md5加密成一个字符串，这样子就代表同一个客户访问的接口。
        $UV=md5($ip.$path);
        //每个IP和接口每分钟不能超过的次数
        $cacheIp=Cache::get($UV)?:0;
        if($cacheIp){
            if($cacheIp>$limit){
                return json_err(-999);
            }else{
                Cache::inc($UV,1);
            }
        }else{
            Cache::set($ip,1,60);
        }
        //将每个请求的IP地址、参数和路径拼接成同一个用户的一个完全相同的接口。
        $post=json_encode(request()->post());
        $name=md5($path.$post);
        //每个相同的数据多少时间内不能请求
        $cache=Cache::get($name);
        if($cache==$ip){
            return json_err(-999);
        }else{
            Cache::set($name,$ip,$time);
            return true;
        }
    }

    // 判断是否是管理员
    public function is_admin(){  return (new PowerSevice())->is_admin($this->userId); }

}
